package com.itzg.filestats;

import java.io.File;

import javax.xml.bind.JAXB;

import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.itzg.fileutil.GaussianSpec;
import com.itzg.fileutil.Scenario;
import com.itzg.fileutil.Scenario.FileSizeSpec;
import com.itzg.fileutil.Scenario.TotalSpec;


class Analyzer implements Runnable {
	private static final Logger logger = LoggerFactory.getLogger(Analyzer.class);
	
	private DescriptiveStatistics fileSize = new DescriptiveStatistics();
	private DescriptiveStatistics filesPerDir = new DescriptiveStatistics();
	private DescriptiveStatistics dirsPerDir = new DescriptiveStatistics();
	private DescriptiveStatistics dirDepth = new DescriptiveStatistics();
	
	private int logAboveDepth = 3;

	private final File startingDirectory;
	
	public Analyzer(File startingDirectory) {
		this.startingDirectory = startingDirectory;
	}
	
	public void analyze() {
		process(startingDirectory, 0);
	}

	private void process(File startingDirectory, int depth) {
		if (depth < logAboveDepth) {
			logger.info("Processing {}...", startingDirectory);
		}
		File[] innerFiles = startingDirectory.listFiles();
		int fileCount = 0;
		int dirCount = 0;
		for (File file : innerFiles) {
			if (!file.isHidden()) {
				if (file.isFile()) {
					++fileCount;
					fileSize.addValue(file.length());
				}
				else if (file.isDirectory()) {
					++dirCount;
					process(file, depth+1);
				}
			}
		}
		filesPerDir.addValue(fileCount);
		dirsPerDir.addValue(dirCount);
		if (dirCount == 0) {
			dirDepth.addValue(depth);
		}
	}

	public void run() {
		analyze();
	}
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("=== File Size ===\n");
		sb.append(fileSize);
		double totalFileSize = fileSize.getSum();
		String units = "bytes";
		if (totalFileSize > 1024*1024) {
			totalFileSize /= 1024*1024;
			units = "MB";
		}
		else if (totalFileSize > 1024) {
			totalFileSize /= 1024;
			units = "KB";
		}
		sb.append("Total: "+totalFileSize+" "+units);
		sb.append("\n=== Files per Directory ===\n");
		sb.append(filesPerDir);
		sb.append("Total: "+filesPerDir.getSum());
		sb.append("\n=== Directories per Directory ===\n");
		sb.append(dirsPerDir);
		sb.append("Total: "+dirsPerDir.getSum());
		sb.append("\n=== Directory Depth ===\n");
		sb.append(dirDepth);
		return sb.toString();
	}

	public DescriptiveStatistics getFileSize() {
		return fileSize;
	}

	public DescriptiveStatistics getFilesPerDir() {
		return filesPerDir;
	}

	public DescriptiveStatistics getDirsPerDir() {
		return dirsPerDir;
	}

	public DescriptiveStatistics getDirDepth() {
		return dirDepth;
	}

	public File getStartingDirectory() {
		return startingDirectory;
	}

	public int getLogAboveDepth() {
		return logAboveDepth;
	}

	public void setLogAboveDepth(int logAboveDepth) {
		this.logAboveDepth = logAboveDepth;
	}

	public void saveToScenarioFile(String scenarioName, File scenarioFileToSave) {
		Scenario scenario = new Scenario();
		scenario.setName(scenarioName);
		scenario.setDepth(new GaussianSpec(dirDepth.getMean(), dirDepth.getStandardDeviation()));
		scenario.setFilesPerDir(new GaussianSpec(filesPerDir.getMean(), filesPerDir.getStandardDeviation()));
		scenario.setDirsPerDir(new GaussianSpec(dirsPerDir.getMean(), dirsPerDir.getStandardDeviation()));
		scenario.setFilesize(new FileSizeSpec(
				new GaussianSpec(fileSize.getMean(), fileSize.getStandardDeviation(), (long) fileSize.getMin()),
				new TotalSpec(0, (long) fileSize.getSum(), 1)));
		scenario.setElements(new TotalSpec(0, (long) (filesPerDir.getSum()+dirsPerDir.getSum()), 1));
		scenario.setLineLength(80);
		JAXB.marshal(scenario, scenarioFileToSave);
	}
}
